package com.ccsoft.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * 2022年7月11日 16:44:11 聊天室服务端
 */
public class Ch10ChatRoomServer {
    private Selector selector;
    private ServerSocketChannel serverSocketChannel;
    private static final int PORT = 6666;

    // 创建对象时自动初始化 NIO 关键组件
    public Ch10ChatRoomServer() throws IOException {
        selector = Selector.open();
        serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.socket().bind(new InetSocketAddress(PORT));
        serverSocketChannel.configureBlocking(false);
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);// 监听客户端连接
    }

    private void listen() throws IOException {
        while(true){// 循环监听
            int count = selector.select(1000);
            if(count <=0){
                System.out.println("服务端监听超过1秒没有接受到客户端连接");
                continue;
            }

            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> keyIterator = selectionKeys.iterator();
            while(keyIterator.hasNext()){// 遍历所有事件
                SelectionKey selectionKey = keyIterator.next();
                SocketChannel socketChannel = (SocketChannel)selectionKey.channel();

                // 连接事件执行的逻辑
                if(selectionKey.isAcceptable()){
                    socketChannel.register(selector,SelectionKey.OP_READ);
                    onlineNotice(socketChannel);
                }

                // 读取 channel 消息，并分发给其他 channel
                if(selectionKey.isReadable()){
                    read(selectionKey);
                }

                keyIterator.remove();// 执行后移除当前事件
            }
        }
    }

    private void onlineNotice(SocketChannel socketChannel) throws IOException {// 上线通知
        System.out.println(socketChannel.getRemoteAddress() + "上线了");
    }

    private void read(SelectionKey selectionKey) {// 从客户端 channel 中读取数据
        SocketChannel socketChannel = (SocketChannel)selectionKey.channel();
        try{
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
            int count  = socketChannel.read(byteBuffer);
            if(count > 0){
                String msg = new String(byteBuffer.array());
                System.out.println("来自[ " + socketChannel.getRemoteAddress()+ " ]的消息：" + msg);
                dispatchMsg(socketChannel,msg);// 把消息分发给其他所有客户端 channel
            }
        } catch (IOException e){
            try{
                System.out.println(socketChannel.getRemoteAddress() + "离线了。。。");
                selectionKey.cancel();// 取消注册
                socketChannel.close(); // 关闭 channel
            }catch (IOException e1){
                e1.printStackTrace();
            }
            e.printStackTrace();
        }
    }

    private void dispatchMsg(SocketChannel self,String msg) throws IOException {// 将 msg 转发到其他客户端上
        Set<SelectionKey> selectionKeys = selector.selectedKeys();
        Iterator<SelectionKey> keyIterator = selectionKeys.iterator();
        while(keyIterator.hasNext()){
            SelectionKey selectionKey = keyIterator.next();
            SocketChannel socketChannel = (SocketChannel)selectionKey.channel();
            // 只将消息分发给其他 channel
            if(socketChannel instanceof SocketChannel && socketChannel == self) continue;

            ByteBuffer byteBuffer = ByteBuffer.wrap(msg.getBytes());
            socketChannel.write(byteBuffer);
        }
    }

    public static void main(String[] args) throws IOException {
        Ch10ChatRoomServer server = new Ch10ChatRoomServer();
        server.listen();
    }

}
